#include "manipulator.h"
#include <QWidget>
#include <QMouseEvent>
#include <complex>
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>

//
// Manipulator
//
Manipulator::Manipulator(const QPoint &base_point, const function_t &x_function, const function_t &y_function, QObject *parent)
: QObject(parent)
, base_point_(base_point)
, x_function_(x_function)
, y_function_(y_function)
{
}

Manipulator::~Manipulator()
{
}

Manipulator *Manipulator::make_linear(const QPoint &base_point, qreal x_scale, qreal x_offset, qreal y_scale, qreal y_offset, QObject *p_parent)
{
  return new Manipulator(
    base_point,
    bind(&Manipulator::linear_function, bind(&QPoint::x, _1), x_scale, x_offset),
    bind(&Manipulator::linear_function, bind(&QPoint::y, _1), y_scale, y_offset),
    p_parent
  );
}

Manipulator *Manipulator::make_exponential(const QPoint &base_point, qreal x_base_val, qreal x_power_base, qreal y_base_val, qreal y_power_base, QObject *p_parent)
{
  return new Manipulator(
    base_point,
    bind(&Manipulator::exponential_function, bind(&QPoint::x, _1), x_base_val, x_power_base),
    bind(&Manipulator::exponential_function, bind(&QPoint::y, _1), y_base_val, y_power_base),
    p_parent
  );
}


void Manipulator::manipulate(const QPoint &pt)
{
  const qreal x = x_function_(pt - base_point_);
  const qreal y = y_function_(pt - base_point_);
  emit manipulate_signal(x, y);
  emit manipulate_x_signal(x);
  emit manipulate_y_signal(y);
}

qreal Manipulator::linear_function(qreal arg, qreal scale, qreal offset)
{
  return arg * scale + offset;
}

qreal Manipulator::exponential_function(qreal arg, qreal base_val, qreal power_base)
{
  return base_val * pow(power_base, arg);
}

//
// Input_filter
//
Input_filter::Input_filter(QWidget *p_widget)
: p_widget_(p_widget)
, p_manipulator_(0)
{
  p_widget_->installEventFilter(this);
}

Input_filter::~Input_filter()
{
}

void Input_filter::reg_manipulator(Qt::MouseButtons mouse_buttons, Qt::KeyboardModifiers keyboard_modifiers, manipulator_creator_t manipulator_creator)
{
  manipulator_map_[qMakePair(mouse_buttons, keyboard_modifiers)] = manipulator_creator;
}

void Input_filter::reg_key_stroke(Qt::Key key, Qt::KeyboardModifiers keyboard_modifiers, key_stroke_t key_stroke)
{
  key_stroke_map_[qMakePair(key, keyboard_modifiers)] = key_stroke;
}

bool Input_filter::eventFilter(QObject *p_object, QEvent *p_event)
{
  bool handled = false;
  switch (p_event->type())
  {
  case QEvent::MouseButtonPress:
    {
      QMouseEvent *p_mouse_event(dynamic_cast<QMouseEvent *>(p_event));
      manipulator_map_t::iterator it = manipulator_map_.find(qMakePair(p_mouse_event->buttons(), p_mouse_event->modifiers()));
      if (it != manipulator_map_.end())
      {
        if (p_manipulator_)
          p_manipulator_->deleteLater();
        p_manipulator_ = (*it)(p_mouse_event->pos());
        p_widget_->setMouseTracking(true);
        handled =  true;
      }
    }
    break;
  case QEvent::MouseButtonRelease:
    if (p_manipulator_)
    {
      p_widget_->setMouseTracking(false);
      p_manipulator_->deleteLater();
      p_manipulator_ = 0;
      handled =  true;
    }
    break;
  case QEvent::MouseMove:
    if (p_manipulator_)
    {
      QMouseEvent *p_mouse_event(dynamic_cast<QMouseEvent *>(p_event));
      p_manipulator_->manipulate(p_mouse_event->pos());
      handled =  true;
    }
    break;
  case QEvent::KeyPress:
    {
      QKeyEvent *p_key_event(dynamic_cast<QKeyEvent *>(p_event));
      key_stroke_map_t::iterator it = key_stroke_map_.find(qMakePair(Qt::Key(p_key_event->key()), p_key_event->modifiers()));
      if (it != key_stroke_map_.end())
      {
        (*it)();
        handled = true;
      }
    }
    break;
  default:
    handled =  false;
  }
  return handled;
}